import kt.RemoteDB;

class App {
	static var seed = Std.random( 100000000 );
	static var object : String = "";

	static var db : RemoteDB;

	public static function main() {
		var now : Float = neko.Sys.time();
		var args : Array<String>= neko.Sys.args();
		db = new RemoteDB();
		db.open();
		var bnum : Int = if( args.length > 0 ) Std.parseInt( args[0] ) else 5000;
		var end : Float = neko.Sys.time();		
		neko.Lib.print( "opening the database:\n");
		neko.Lib.print("time : " + ( (end - now) )  + "\n" );

		var traceEnabled = false; // disabling trace allows for + ~7-8% higher performance
		object = Info.add(); // implies haxe serialization cost
//		object = Std.random(1000000) + ""; // simpler = faster : but not really "Real Life" example...

		db.clear();

		// short version : binary bulk is way faster than any other ( bulk or single operations ),
		// even when inserting just one record
		var by = 100; // bigger is faster :) try 500 or 1000 of higher bnum value
		testBinaryBulk( "setting multiple ("+ by+") records insertion using binary bulk: ", by, bnum, traceEnabled );
		

		// short version : when you want to insert an object
		// it's better to insert one record by field even if serialization occur for some fields 
		// than to put only one record with the whole object serialized
		testMultipleBinaryVsSerialization( bnum, traceEnabled );

		var f = function( k : String, v : String ) {
			db.set( k,v );
		}
		test( "setting records:", f, bnum, traceEnabled );

		f = function( k : String, v : String ) {
			db.add( k,v );
		}
		test( "adding records:", f, bnum, traceEnabled );

		f = function( k : String, v : String ) {
			db.replace( k,v );
		}
		test( "replacing records:", f, bnum, traceEnabled );

		f = function( k : String, v : String ) {
			db.get( k );
		}
		test( "getting records:", f, bnum, traceEnabled );

		f = function( k : String, v : String ) {
			var h = new Hash();
			h.set(k,v);
			db.setBulk( h );
		}
		test( "settting single records using bulk:", f, bnum, traceEnabled );

		f = function( k : String, v : String ) {
			if( k == null  ) return;			
			var b : BulkRecord = {key:"i"+k,value:"i"+v,expirationTime:0,dbidx:0};
			db.setBulkBinary( [b] );
		}

		neko.Lib.print( "\n");
		var info = db.status();
		for( k in info.keys() ) {
			neko.Lib.print( k + ":" + info.get(k) + "\n");
		}
		db.close();
		neko.Lib.print( "\n");
	}

	static function test( title, cbk : String -> String -> Void, bnum : Int, traceEnabled = true ) {		
		neko.Lib.print(title+": (for "+bnum+" records) \n");
		var tth = Std.int( bnum / 10 );
		var next = tth;
		var sth = Std.int( tth / 25 );
		var snext = sth;
		var now = neko.Sys.time();
		for( i in 0...bnum ) {
			if( traceEnabled ) {
				if( i > snext ) {
					neko.Lib.print( ".");
					snext += sth;
				}
				if( i > next ) {
					neko.Lib.print( " ("+next+")\n");
					next += tth;
				}
			}
			//cbk( i+"", "1" );
			cbk(i+"",object);
		}
		var end = neko.Sys.time();
		neko.Lib.print( " ("+bnum+")\n");
		var r = (end - now);
		neko.Lib.print( "time:" + r  + "\n");
		neko.Lib.print( "QPS:" +  (bnum/r)  + "\n");
	}

	static function test2( title, cbk : Int -> Void, bnum : Int, traceEnabled = true ) {		
		neko.Lib.print(title+": (for "+bnum+" records) \n");
		var tth = Std.int( bnum / 10 );
		var next = tth;
		var sth = Std.int( tth / 25 );
		var snext = sth;
		var now = neko.Sys.time();
		for( i in 0...bnum ) {
			if( traceEnabled ) {
				if( i > snext ) {
					neko.Lib.print( ".");
					snext += sth;
				}
				if( i > next ) {
					neko.Lib.print( " ("+next+")\n");
					next += tth;
				}
			}
			cbk(i);
		}
		var end = neko.Sys.time();
		neko.Lib.print( " ("+bnum+")\n");
		var r = (end - now);
		neko.Lib.print( "time:" + r  + "\n");
		neko.Lib.print( "QPS:" +  (bnum/r)  + "\n");
	}

	static function testMultipleBinaryVsSerialization( bnum : Int, traceEnabled = true ) {
		var info = new Info(); 
		var f = function( i : Int ) {
			var sinfo = haxe.Serializer.run( info );
			var br : BulkRecord = { key:i+"ms", value:sinfo,expirationTime:0,dbidx:0 };
			db.setBulkBinary( [br] );
		}
		var f2 = function( i : Int ) {
			var a = [
				{key:i+"bms_text", value : info.text, expirationTime:0, dbidx:0 },
				{key:i+"bms_id", value : info.id + "", expirationTime:0, dbidx:0 },
				{key:i+"bms_name", value : info.name + "", expirationTime:0, dbidx:0 },
				{key:i+"bms_length", value : Std.string(info.length), expirationTime:0, dbidx:0 },
				{key:i+"bms_data", value : haxe.Serializer.run(info.data), expirationTime:0, dbidx:0 },
			];

			/* // Reflect is way too slow
			for( f in Reflect.fields( info ) ) {
				var sinfo = haxe.Serializer.run( Reflect.field( info, f ) );
				var br : BulkRecord = { key:i+"bms", value:sinfo,expirationTime:0,dbidx:0 };
				a.push( br ); 
			}*/
			db.setBulkBinary( a );
		}
		test2( "Inserting " +bnum+ " big serialized objects", f, bnum, traceEnabled );
		test2( "Inserting " +bnum+ " objects, one record by field", f2, bnum, traceEnabled );
		
	}

	static function testBinaryBulk( title, by : Int, bnum : Int, traceEnabled = true) {		
		neko.Lib.print(title+": (for "+bnum+" records) \n");
		var tth = Std.int( bnum / 10 );
		var next = Std.int(tth/by);
		var snext = next;
		var now = neko.Sys.time();
		var iter = Std.int( bnum/by);
		for( i in 0...iter ) {
			var a = new Array();
			for( j in 0...by ) {
				var b : kt.BulkRecord = {key:i+"bin"+j, value:object, expirationTime:0, dbidx:0};
				a.push( b );
			}
			db.setBulkBinary(a);
			if( i >= next ) {
				if( traceEnabled ) neko.Lib.print( " ("+ i * by+")\n");
				next += snext;
			}
			if( traceEnabled ) neko.Lib.print( "." );
		}
		var end = neko.Sys.time();
		neko.Lib.print( " ("+bnum+")\n");
		var r = (end - now);
		neko.Lib.print( "time:" + r  + "\n");
		neko.Lib.print( "QPS:" +  (bnum/r)  + "\n");
	}
	
}
